package com.hp.codegen.processor;

import com.hp.codegen.context.CodeGenContextHolder;
import com.hp.codegen.spi.CodeGenProcessor;
import com.hp.common.base.annotation.MethodDesc;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.file.Paths;

/**
 * @author hp
 */
@Getter
@Slf4j
@RequiredArgsConstructor
public abstract class AbstractCodeGenProcessor implements CodeGenProcessor {

    protected static final String FILE_COMMENT = "--- Auto Generated By CodeGen Module ---";

    protected String currentGeneratingTypeName;
    protected ClassName currentGeneratingClassName;

    @Override
    public void init(TypeElement typeElement, RoundEnvironment roundEnvironment) {
        this.currentGeneratingTypeName = CodeGenContextHolder.getClassSimpleName(getAnnotation());
        this.currentGeneratingClassName = CodeGenContextHolder.getClassName(getAnnotation());
    }

    @Override
    public void generate(TypeElement typeElement, RoundEnvironment roundEnvironment) {
        if (!creatable(typeElement, roundEnvironment)) {
            return;
        }
        final TypeSpec typeSpec = createType(typeElement, roundEnvironment);
        generateFile(typeSpec);
    }

    protected boolean creatable(TypeElement typeElement, RoundEnvironment roundEnvironment) {
        return true;
    }

    protected TypeSpec createType(TypeElement typeElement, RoundEnvironment roundEnvironment) {
        final TypeSpec.Builder typeSpecBuilder = isInterface() ? TypeSpec.interfaceBuilder(this.currentGeneratingTypeName) : TypeSpec.classBuilder(this.currentGeneratingTypeName);

        genericCustomization(typeElement, roundEnvironment, typeSpecBuilder);

        customizeType(typeElement, roundEnvironment, typeSpecBuilder);

        return typeSpecBuilder.build();
    }

    protected boolean isInterface() {
        return false;
    }

    private void genericCustomization(TypeElement typeElement, RoundEnvironment roundEnvironment, TypeSpec.Builder typeSpecBuilder) {
        final String user = System.getProperty("user.name");
        typeSpecBuilder.addJavadoc("@author " + user);
    }

    protected abstract void customizeType(TypeElement typeElement, RoundEnvironment roundEnvironment, TypeSpec.Builder typeSpecBuilder);

    protected void generateFile(TypeSpec typeSpec) {
        final Class<? extends Annotation> annotation = getAnnotation();
        final JavaFile javaFile = JavaFile.builder(CodeGenContextHolder.getPackageName(annotation), typeSpec)
                .addFileComment(FILE_COMMENT)
                .indent("    ")
                .build();

        if (CodeGenContextHolder.isGenerateToSource(annotation)) {
            writeToSource(javaFile, CodeGenContextHolder.getPackageName(annotation), CodeGenContextHolder.getSourcePath(annotation));
        } else {
            writeToCompile(javaFile);
        }
    }

    @MethodDesc("写到编译目录下")
    private void writeToCompile(JavaFile javaFile) {
        try {
            javaFile.writeTo(CodeGenContextHolder.getEnvironment().getFiler());
        } catch (IOException e) {
            CodeGenContextHolder.getEnvironment().getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
        }
    }

    @MethodDesc("写到源码目录下")
    private void writeToSource(JavaFile javaFile, String packageName, String sourcePath) {
        final String fileName = javaFile.typeSpec.name + ".java";
        final String packagePath = packageName.replace(".", File.separator) + File.separator + fileName;
        try {
            final String absolutePath = Paths.get(sourcePath).toFile().getAbsolutePath();
            File file = new File(absolutePath);
            if (!file.exists()) {
                return;
            }
            String sourceFileName = absolutePath + File.separator + packagePath;
            File sourceFile = new File(sourceFileName);
            if (!sourceFile.exists()) {
                javaFile.writeTo(file);
            }
        } catch (IOException e) {
            log.error("generate Java Source File error", e);
        }
    }

}
